package com.cropdoctor.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.cropdoctor.common.CustomException;
import com.cropdoctor.common.EsMsgHandler;
import com.cropdoctor.common.Result;
import com.cropdoctor.mapper.*;
import com.cropdoctor.model.constants.PageParams;
import com.cropdoctor.model.constants.PageResult;
import com.cropdoctor.model.dto.*;
import com.cropdoctor.model.po.*;
import com.cropdoctor.model.vo.PublishCourseInfoVO;
import com.cropdoctor.model.vo.PublishCourseVO;
import com.cropdoctor.service.CourseService;
import com.cropdoctor.util.ContextGet;

import io.redisearch.Document;
import io.redisearch.Query;
import io.redisearch.SearchResult;
import io.redisearch.client.Client;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

@Service
public class CourseServiceImpl implements CourseService {
    @Autowired
    PublishCourseMapper publishCourseMapper;

    @Autowired
    VideoMapper videoMapper;

    @Autowired
    PrePublishCourseMapper prePublishCourseMapper;

    final static String COURSE_KEY = "course";
    @Autowired
    RabbitTemplate rabbitTemplate;

    @Autowired
    RedisTemplate redisTemplate;

    @Autowired
    CourseInfoMapper courseInfoMapper;

    @Autowired
    AuditHistoryMapper auditHistoryMapper;

    @Autowired
    @Qualifier("courseSearchClient")
    Client redisSearchClient;

    @Override
    @Transactional
    public Result<String> auditCourse(String auditStatus, String auditMind, String id) {
        LambdaUpdateWrapper<PrePublishCoursePo> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(PrePublishCoursePo::getId,id).
                set(auditMind!=null, PrePublishCoursePo::getAuditMind,auditMind). //此处更新的是预发布表
                set(PrePublishCoursePo::getAuditStatus,auditStatus);
        int result = prePublishCourseMapper.update(null, wrapper);
        if(result <= 0) throw new CustomException("审核状态更新失败!");
        LambdaUpdateWrapper<CourseInfoPo> wrapper2 = new LambdaUpdateWrapper<>();
        wrapper2.eq(CourseInfoPo::getId,id).
                set(auditMind!=null, CourseInfoPo::getAuditMind,auditMind). //此处更新的是课程基本信息
                set(CourseInfoPo::getAuditStatus,auditStatus);
        result = courseInfoMapper.update(null,wrapper2);
        if(result <= 0) throw new CustomException("审核状态更新失败!");

        return Result.success("审核状态更新成功!");
    }

    @Override
    public Result<List<PrePublishCoursePo>> getAllPrePublishCourse() {
        LambdaQueryWrapper<PrePublishCoursePo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(PrePublishCoursePo::getAuditStatus,"103");//只获取未审核的课程
        List<PrePublishCoursePo> list = prePublishCourseMapper.selectList(wrapper);
        return Result.success(list);
    }

    @Override
    public Result<List<AuditHistoryDto>> getAuditHistory(LocalDate endDate, LocalDate beginDate) {
        LambdaQueryWrapper<AuditHistoryPo> wrapper = new LambdaQueryWrapper<>();
//        wrapper.le(endDate != null,AuditHistoryPo::getCreateDate,endDate).ge(beginDate!=null,AuditHistoryPo::getCreateDate,beginDate);
        List<AuditHistoryPo> auditHistoryPos = auditHistoryMapper.selectList(wrapper);
        ArrayList<AuditHistoryDto> auditHistoryDtos = new ArrayList<>();
        auditHistoryPos.forEach(item->{
            AuditHistoryDto auditHistoryDto = new AuditHistoryDto();
            BeanUtils.copyProperties(item,auditHistoryDto);
            auditHistoryDtos.add(auditHistoryDto);
        });
        return Result.success(auditHistoryDtos);
    }

    @Override
    public Result<List<PublishCourseVO>> getUploadCourse() {
        LambdaQueryWrapper<PublishCoursePo> wrapper = new LambdaQueryWrapper<>();

        long userId = ContextGet.getUserId();
        List<PublishCourseVO> list = publishCourseMapper.getPublishCourseList(userId);
        return Result.success(list);
    }


    @Override
    @Transactional
    public Result<String> submitCourseInfo(CourseInfoPo poFromUser) {
        long courseId = poFromUser.getId();//对应课程id
        CourseInfoPo courseInfoPo = courseInfoMapper.selectById(courseId);
        int result1;
        if(courseInfoPo == null){
            long userId = ContextGet.getUserId();
            courseInfoPo = new CourseInfoPo();
            BeanUtils.copyProperties(poFromUser,courseInfoPo);
            courseInfoPo.setUploaderId(userId);
            courseInfoPo.setAuditStatus("103");//待审核
            courseInfoPo.setSubmitStatus("302");//已提交
            courseInfoPo.setPublishStatus("201");//状态为未发布
            result1 = courseInfoMapper.insert(courseInfoPo);
        }else{
            //未提交的课程才可以再次提交
            if("302".equals(courseInfoPo.getSubmitStatus())) throw new CustomException("本课程已提交 请等待审核!");
            courseInfoPo.setAuditStatus("103");//待审核
            courseInfoPo.setSubmitStatus("302");//已提交
            courseInfoPo.setPublishStatus("201");//状态为未发布
            result1 = courseInfoMapper.updateById(courseInfoPo);//更新到课程基本信息表
        }
        if(result1 <= 0) throw new CustomException("提交失败!");

        PrePublishCourseDto prePublishCourseDto = new PrePublishCourseDto();
        BeanUtils.copyProperties(courseInfoPo,prePublishCourseDto);
        prePublishCourseDto.setPublishStatus("201");//未发布
        prePublishCourseDto.setAuditStatus("103");//更新状态为待审核
        int result2 = 0;
        if(prePublishCourseMapper.selectById(courseId) != null){//预发布表中已经有提交的记录 则更新预发布表即可
            result2 = prePublishCourseMapper.updateById(prePublishCourseDto);
        }else{
            result2 = prePublishCourseMapper.insert(prePublishCourseDto);//插入课程预发布表中
        }
        if(result2 <= 0) throw new CustomException("提交失败!"); //报错 事务回滚
        return Result.success("提交成功,等待审核中");
    }

    @Override
    public Result<String> saveCourseInfo(CourseInfoPo poFromUser) {
        CourseInfoPo courseInfoPo = courseInfoMapper.selectById(poFromUser.getId());
        int result;
        if(courseInfoPo == null){
            long userId = ContextGet.getUserId();
            courseInfoPo = new CourseInfoPo();
            BeanUtils.copyProperties(poFromUser,courseInfoPo);
            courseInfoPo.setUploaderId(userId);
            courseInfoPo.setSubmitStatus("301");//每次保存都重新更新状态为未提交
            courseInfoPo.setAuditStatus("103");//待审核
            courseInfoPo.setPublishStatus("201");//未发布
            result = courseInfoMapper.insert(courseInfoPo);
        }else{//如果记录已经存在则更新
            BeanUtils.copyProperties(poFromUser,courseInfoPo);
            courseInfoPo.setSubmitStatus("301");//每次保存都重新更新状态为未提交
            courseInfoPo.setAuditStatus("103");//待审核
            courseInfoPo.setPublishStatus("201");//未发布
            result = courseInfoMapper.updateById(courseInfoPo);
        }

        return result <= 0 ? Result.error("保存失败!") : Result.success("保存成功!");
    }

    @Override
    @Transactional
    public Result<String> uploadCourse(CourseInfoPo poFromUser) {
        //是从课程预发布表中查询!!
        long courseId = poFromUser.getId();
        PrePublishCoursePo prePublishCoursePo = prePublishCourseMapper.selectById(courseId); //从预发布表中查询

        if(prePublishCoursePo == null) throw new CustomException("查询不到该课程!");

        if("101".equals(prePublishCoursePo.getAuditStatus())){//审核通过
            int update = courseInfoMapper.update(null, new LambdaUpdateWrapper<CourseInfoPo>().
                    eq(CourseInfoPo::getId, courseId).
                    set(CourseInfoPo::getPublishStatus, "202"));
            if(update <= 0) throw new CustomException("上传失败!");

            if("202".equals(prePublishCoursePo.getPublishStatus())) throw new CustomException("科普视频已发布过!");

            PublishCoursePo publishCoursePo = new PublishCoursePo();
            BeanUtils.copyProperties(prePublishCoursePo, publishCoursePo);//将预发布表中审核通过的信息插入到正式发布表中
            publishCoursePo.setStatus("1");//默认展示
            publishCoursePo.setCreateDate(LocalDate.now());
            int result;

            PublishCourseVO publishCourseVO = new PublishCourseVO(
                    poFromUser.getTitle(),
                    poFromUser.getUrl(),
                    poFromUser.getId(),
                    "1",//默认展示
                    publishCoursePo.getCreateDate());
            if(publishCourseMapper.selectById(courseId) == null){//正式课程表中不存在该记录
                result = publishCourseMapper.insert(publishCoursePo);//插入到正式课程表中
                addToRefreshCourseCache(publishCourseVO);//更新课程缓存
                addToRefreshMtOrStCache(poFromUser.getMt(),poFromUser.getSt(),publishCourseVO);//更新对应分类缓存
            }else {
                result = publishCourseMapper.updateById(publishCoursePo);//否则更新
                refreshCourseCache();
                refreshCategoryCache();
            }
            if(result <= 0) throw new CustomException("上传失败!");

            result = prePublishCourseMapper.deleteById(courseId);//将预发布表中的信息删除
            if(result <= 0) throw new CustomException("上传失败!");

            HashMap<String, Object> map = new HashMap<>();
            map.put("id",courseId);
            map.put("Po", JSON.toJSONString(publishCoursePo));
            map.put("type","1");//新增索引操作
            map.put("sqlTable","publish_course");//告诉错误消息出问题的是哪一个表中的主键
            rabbitTemplate.convertAndSend(EsMsgHandler.EXCHANGE_TOPIC,EsMsgHandler.ROUTINGKEY_COURSE,map);

            return Result.success("发布成功!");
        }else{
            return Result.error("审核不通过无法发布课程");
        }
    }

    @Override
    @Transactional
    public Result<String> removeCourse(long courseId) {
        PublishCoursePo publishCoursePo = publishCourseMapper.selectById(courseId);
        if(publishCoursePo.getUploaderId() != ContextGet.getUserId()) throw new CustomException("只能下架自己发布的课程");
        LambdaUpdateWrapper<PublishCoursePo> wrapper = new LambdaUpdateWrapper<>();
        publishCoursePo.setStatus("0");
        wrapper.eq(PublishCoursePo::getId,courseId).set(PublishCoursePo::getStatus,0);
        int update = publishCourseMapper.update(null,wrapper);
        if(update <= 0) throw new CustomException("上线失败!");

        HashMap<String, Object> map = new HashMap<>();
        map.put("id",courseId);
        map.put("Po", JSON.toJSONString(publishCoursePo));
        map.put("type","1");//更新索引操作
        map.put("sqlTable","publish_course");//告诉错误消息出问题的是哪一个表中的主键
        rabbitTemplate.convertAndSend(EsMsgHandler.EXCHANGE_TOPIC,EsMsgHandler.ROUTINGKEY_COURSE,map);

        refreshCategoryCache();
        refreshCourseCache();

        //更改状态为不展示即可
        return Result.success("下架成功!");
    }

    public void refreshCategoryCache(){
        Set<String> keys = redisTemplate.keys("course_*");//批量删除以course_为前缀的key
        redisTemplate.delete(keys);
    }

    public void refreshCourseCache(){
        redisTemplate.delete(COURSE_KEY);
        LambdaQueryWrapper<PublishCoursePo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(PublishCoursePo::getStatus,"1");
        List<PublishCourseVO> voList = publishCourseMapper.selectPublishCourseListWithUrl(wrapper);
        if(voList != null && voList.size() != 0) redisTemplate.opsForList().rightPushAll(COURSE_KEY,voList);
    }

    //新增缓存
    public void addToRefreshCourseCache(PublishCourseVO publishCourseVO){
        redisTemplate.opsForList().rightPush(COURSE_KEY,publishCourseVO);
    }

    //新增缓存
    public void addToRefreshMtOrStCache(String mt,String st,PublishCourseVO publishCourseVO){
        if(!"".equals(mt)) {
            redisTemplate.opsForList().rightPush("course_"+mt,publishCourseVO);
        }
        if(!"".equals(st)) {
            redisTemplate.opsForList().rightPush("course_"+st,publishCourseVO);
        }
    }


    @Override
    public Result<PrePublishCourseDto> getPrePublishCourseInfo(Long courseId) {
        PrePublishCourseDto prePublishCourseDto = prePublishCourseMapper.getInfo(courseId);
        return Result.success(prePublishCourseDto);
    }


    public PageResult<PublishCourseVO> getALL(String key, PageParams pageParams){
        long start = (pageParams.getPageNo() - 1) * pageParams.getPageSize();//起始位置
        Long total = redisTemplate.opsForList().size(key); //此处获得是所有的数据
        if(total == 0) {//缓存中无数据 从数据库中更新到缓存上
            LambdaQueryWrapper<PublishCoursePo> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(PublishCoursePo::getStatus,"1");
            List<PublishCourseVO> list = publishCourseMapper.selectPublishCourseListWithUrl(wrapper);
            total = Long.valueOf(list.size());//数据总量
            if(list != null && list.size() != 0) redisTemplate.opsForList().rightPushAll(key,list);//存入缓存
        }
        //封装返回结果类
        long end = total > start + pageParams.getPageSize() ? start + pageParams.getPageSize() - 1 : total - 1;
        List<PublishCourseVO> voList = redisTemplate.opsForList().range(key, start, end);

        return new PageResult<PublishCourseVO>(voList,total,pageParams.getPageNo(),pageParams.getPageSize());
    }

    public PageResult<PublishCourseVO> getWithKeyword(String keyword, String mt, String st, PageParams pageParams) {
        StringBuilder q = new StringBuilder();
        q.append(keyword + " ");
        q.append("@status:{1} ");//必须是上架的
        if (!"".equals(mt)) {
            q.append("@mt:" + "{" + mt + "} ");
        }
        if (!"".equals(st)) {
            q.append("@st:" + "{" + st + "}");
        }
        Query query = new Query(q.toString());

        int offset = (int) ((pageParams.getPageNo() - 1) * pageParams.getPageSize());
        int num = pageParams.getPageSize().intValue();
        query.limit(offset, num);

        SearchResult search = redisSearchClient.search(query);
        List<Document> docs = search.docs;
        List<PublishCourseVO> list = new ArrayList<>();
        for (Document doc : docs) {
            DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd");
            LocalDate dateTime = LocalDate.parse(doc.getString("createDate"), df);
            PublishCourseVO publishCourseVO = PublishCourseVO.builder()
                    .id(Long.valueOf(doc.getString("id")))
                    .title(doc.getString("title"))
                    .url(doc.getString("url"))
                    .createDate(dateTime)
                    .build();
            list.add(publishCourseVO);
        }

        return new PageResult<>(list, search.totalResults, pageParams.getPageNo(),pageParams.getPageSize());
    }


    public PageResult<PublishCourseVO> getByCategory(String mt,String st,PageParams pageParams){
        long start = (pageParams.getPageNo() - 1) * pageParams.getPageSize();//起始位置
        Long total = 0L;
        String key = !"".equals(mt) ? mt : st;
        total = redisTemplate.opsForList().size("course_"+key);//查询果蔬课程总体数据量

        if(total == 0){
            //缓存中并没有数据
            LambdaQueryWrapper<PublishCoursePo> wrapper = new LambdaQueryWrapper<>();
            wrapper
                    .eq(!"".equals(mt), PublishCoursePo::getMt,mt)
                    .eq(!"".equals(st), PublishCoursePo::getSt,st)
                    .eq(PublishCoursePo::getStatus,"1");
            List<PublishCourseVO> pos = publishCourseMapper.selectPublishCourseListWithUrl(wrapper);
            total = Long.valueOf(pos.size());//数据总量
            if(total != 0) redisTemplate.opsForList().rightPushAll("course_"+key,pos);//存入缓存
            else return new PageResult<PublishCourseVO>(new ArrayList<>(),0,pageParams.getPageNo(),pageParams.getPageSize());
        }
        long end = total > start + pageParams.getPageSize() ? start + pageParams.getPageSize() - 1 : total - 1;
        List<PublishCourseVO> voList = redisTemplate.opsForList().range("course_"+key, start, end);

        return new PageResult<PublishCourseVO>(voList,total,pageParams.getPageNo(),pageParams.getPageSize());
    }


    private PageResult<PublishCourseVO> select(String mt, String st,String keyword,PageParams pageParams){
        if("".equals(keyword)){
            if(!"".equals(mt)|| !"".equals(st)){//仅分类参与搜索数据
                return getByCategory(mt,st,pageParams);
            }else{
                //需要全部数据
                return getALL(COURSE_KEY, pageParams);
            }
        }else{//有关键词 缓存方案不适用
            return getWithKeyword(keyword,mt,st,pageParams);
        }
    }

    @Override
    public Result<PageResult<PublishCourseVO>> getCourseList(String mt, String st, String keyword, PageParams pageParams) {
        return Result.success(select(mt, st,keyword,pageParams));
    }

    @Override
    public Result<PublishCourseInfoVO> getCourseInfo(long id) {
        return Result.success(publishCourseMapper.getCourseInfo(id));
    }

    @Override
    public Result<List<CourseInfoPo>> getMyCourseInfo(String auditStatus) {
        if("101".equals(auditStatus) || "102".equals(auditStatus)){//审核通过或不通过都应该从prepublish里查 因为预发布表才是被审核过的表
            return Result.success(prePublishCourseMapper.getMyCourseInfo(ContextGet.getUserId(),auditStatus));
        }
        return Result.success(courseInfoMapper.getMyCourseInfo(ContextGet.getUserId(),auditStatus));
    }

    @Override
    public Result<CourseInfoPo> getSingleCourseInfo(long courseId,String index) {
        if("1".equals(index)){ //查看审核通过详细记录
            return Result.success(prePublishCourseMapper.getSingleCourseInfo(courseId,"101"));
        }
        if("2".equals(index)){ //查看审核失败的详细记录
            return Result.success(prePublishCourseMapper.getSingleCourseInfo(courseId,"102"));
        }
        if("3".equals(index)){ //查看已发布课程的详细记录
            return Result.success(publishCourseMapper.getSingleCourseInfo(courseId));
        }
        return Result.success(courseInfoMapper.getSingleCourseInfo(courseId));
    }

    @Override
    public Result<String> reEditCourse(Long id) {
        int result = prePublishCourseMapper.deleteById(id);//直接从预发布表中删除即可
        LambdaUpdateWrapper<CourseInfoPo> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(CourseInfoPo::getId,id).set(CourseInfoPo::getAuditStatus,"103")//更改状态为待审核
                .set(CourseInfoPo::getAuditMind,"")//清空审核建议
                .set(CourseInfoPo::getSubmitStatus,"301");//未提交
        int update = courseInfoMapper.update(null, wrapper);
        if(!(update > 0)) return Result.error("重新编辑失败!");
        return result > 0 ? Result.success("重新编辑成功!") : Result.error("重新编辑失败!");
    }

    @Override
    public Result<String> upCourse(long courseId) {
        PublishCoursePo publishCoursePo = publishCourseMapper.selectById(courseId);
        if(publishCoursePo.getUploaderId() != ContextGet.getUserId()) throw new CustomException("只能重新上线自己发布的课程");
        LambdaUpdateWrapper<PublishCoursePo> wrapper = new LambdaUpdateWrapper<PublishCoursePo>();
        publishCoursePo.setStatus("1");
        wrapper.eq(PublishCoursePo::getId,courseId).set(PublishCoursePo::getStatus,1);
        int update = publishCourseMapper.update(null,wrapper);
        if(update <= 0) throw new CustomException("上线失败!");

        HashMap<String, Object> map = new HashMap<>();
        map.put("id",courseId);
        map.put("Po", JSON.toJSONString(publishCoursePo));
        map.put("type","1");//更新索引操作
        map.put("sqlTable","publish_course");//告诉错误消息出问题的是哪一个表中的主键
        rabbitTemplate.convertAndSend(EsMsgHandler.EXCHANGE_TOPIC,EsMsgHandler.ROUTINGKEY_COURSE,map);

        refreshCourseCache();
        refreshCategoryCache();
        //更改状态为不展示即可
        return Result.success("上线成功!");
    }

    @Override
    @Transactional
    public Result<String> deleteCourseInfo(long id) {
        int result;
        PublishCoursePo publishCoursePo = publishCourseMapper.selectById(id);
        if(publishCoursePo != null){
            result = publishCourseMapper.deleteById(id);
            if(result <= 0) throw new CustomException("删除失败!");
            //删除es搜索库中的信息
            HashMap<String, Object> map = new HashMap<>();
            map.put("id",id);
            map.put("type","0");//删除索引操作
            rabbitTemplate.convertAndSend(EsMsgHandler.EXCHANGE_TOPIC,EsMsgHandler.ROUTINGKEY_COURSE,map);
        }
        PrePublishCoursePo prePublishCoursePo = prePublishCourseMapper.selectById(id);
        if(prePublishCoursePo != null){
            result = prePublishCourseMapper.deleteById(id);
            if(result <= 0) throw new CustomException("删除失败!");
        }
        result = courseInfoMapper.deleteById(id);
        refreshCourseCache();
        refreshCategoryCache();
        return result > 0 ? Result.success("删除成功!") : Result.error("删除失败!");
    }
}
